var koa = require('koa');
var _ = require('lodash');
var path = require('path');
var crypto = require('crypto');
var swig = require('swig');
var cluster = require('cluster');
var gzip = require('koa-gzip');
var koaBody = require('koa-body');
var session = require('koa-generic-session');
var redisStore = require('koa-redis');
var minifier = require('koa-html-minifier');
var assets = require('koa-static-cache');
var compose = require('koa-compose');
var config = require('./config');
var interceptor = require('./config/interceptor');
var support = require('./support');
var logger = require('./support/logger');
var accountService = require('./modules/account/service');

// 加载swig过滤器
require('./config/filter');

/**
 * KOA INIT
 */
var app = module.exports = koa();

// GZIP压缩文档
app.use(gzip());

// 访问静态资源
app.use(compose([

  function * (next) {
    if (this.path.indexOf('/static/') === 0) {
      this.path = this.path.replace(/-\$[\d\w]+\$/i, '');
    }
    yield * next;
  },
  assets(config.static, {
    maxAge: 365 * 24 * 60 * 60,
    gzip: true,
    prefix: '/static'
  })
]));

// 设置秘钥
app.keys = ['dee~642@e8c*b1cca^5!2aae0@2a4dd!'];

// 设置SESSION
app.use(session({
  key: 'M',
  ttl: 2 * 60 * 60 * 1000, // 2小时缓存
  store: redisStore({
    host: config.redis.host,
    port: config.redis.port,
  }),
  cookie: {
    maxage: 365 * 24 * 60 * 60 * 1000
  }
}));

// 去除HTML页面中的换行和空白
app.use(minifier({
  minifyJS: true,
  minifyCSS: true,
  collapseWhitespace: true,
  keepClosingSlash: true,
  removeComments: true,
  processScripts: ['text/swig-template']
}));

// 解析form
app.use(koaBody({
  strict: false,
  multipart: true,
  formidable: {
    uploadDir: path.join(__dirname, '../upload')
  }
}));

// 判断是否是内嵌浏览器
app.use(function * (next) {
  var userAgent = (this.headers['user-agent'] || '').toLowerCase();
  // 客户端判断
  this.ua = {
    ios: userAgent.indexOf('mobile/') !== -1 && userAgent.indexOf('safari/') === -1,
    android: ['com.qding.community', 'com.qding.guanjia'].indexOf(this.headers['x-requested-with'] || '') !== -1 || this.headers['qdPlatform'] === 'android',
  };

  // 是否是APP
  this.ua.app = (this.ua.ios || this.ua.android);

  // 是否是Ajax
  this.request.isAjax = this.headers['x-requested-with'] === 'XMLHttpRequest';

  yield next;
});

// 设置模板引擎
app.use(function * (next) {
  var ticket = null;
  this.render = function (name) {
    // 构造数据
    var data = {};

    // 字符串处理
    if (_.isString(arguments[1])) {
      data.message = arguments[1];
    }
    // 合并数据
    else {
      _.merge.apply(_, [data].concat(Array.prototype.splice.call(arguments, 1)));
    }

    // 返回接口
    if (_.isNumber(name)) {
      return {
        code: name,
        data: data
      }
    }

    // 返回页面
    if (/^[^./]/i.test(name)) {
      var folder = path.dirname(support.callsite()[1].getFileName());
      name = path.resolve(folder, name);
    }

    // 添加UA数据
    data = _.merge(data, {
      ua: this.ua,
      nhost: this.host,
      whost: config.whost,
      nested: this.ua.ios || this.ua.android,
    });

    return swig.renderFile(name, data);
  };

  // 兼容旧版本
  this.template = {
    render: this.render.bind(this)
  };

  yield next;
});

// 全局错误处理
app.use(function * (next) {
  var error = null;
  try {
    logger.info('Request:', {
      url: this.request.url,
      member: (this.session.member && this.session.member.memberId) || this.query.memberId || 'anonymous',
      ip: this.request.headers['x-real-ip'],
    });

    yield * next;
  }
  catch (e) {
    error = e;
    this.status = e.status || 500;
  }
  finally {
    this.status = this.status === 405 ? 404 : this.status;
    if (this.status !== 200) {
      // 打印400及以上的错误日志
      if (this.status >= 400 || error) {
        logger.error(error || this.status, {
          url: this.request.url,
          member: (this.session.member && this.session.member.memberId) || 'anonymous',
          ip: this.request.headers['x-real-ip'],
        });
      }
      if (this.status === 404 || error) {
        var mime = this.accepts(['json', 'html']);
        if (mime === 'json') {
          this.body = this.template.render(this.status, this.status === 404 ? '抱歉，服务未找到' : '服务器异常，请稍后再试');
          this.status = 200;
        }
        else {
          this.body = this.template.render(this.status === 404 ? '../app/modules/common/templates/404.html' : '../app/modules/common/templates/500.html');
        }
      }
      else if (this.status === 403) {
        this.body = this.template.render('../app/modules/common/templates/403.html');
      }
    }
  }
});

// 覆写SESSION 不能简单session = { ... }; 坑爹的BUG
function * sessionRewrite(data) {
  yield this.regenerateSession();
  _.merge(this.session, data);
  return this.session;
}

/**
 * 拦截从APP过来的请求，替换用户
 */
app.use(function * (next) {
  var query = this.query;

  // 链接自带身份信息并给出签名信息
  if (query.mId && query.aId && query.sign) {
    var params = [];
    Object.keys(query).sort().forEach(function (name) {
      if (name !== 'sign') {
        params.push(name + '=' + encodeURIComponent(query[name]));
      }
    });
    var mysign = crypto.createHash('md5').update(params.join('&') + app.keys[0]).digest('hex');
    if (mysign === query.sign) {
      // 临时保存 防止TOKEN过期 用这些来刷新TOKEN
      _.merge(this.session, query);
      // 校验通过
      var entity = yield accountService.getMember(this, query.mId, query.aId);
      // 遍历身份列表，缓存商户id
      var vendorId = 0;
      entity.partnerUserInfoList.forEach(function (userInfo) {
        if (userInfo.partnerType == 'sh') {
          return vendorId = userInfo.partnerId;
        }
      });
      var puser = entity.partnerUserInfoList.filter(function (item) {
        return item.partnerType === 'bs';
      })[0];
      this.session = yield sessionRewrite.call(this, {
        member: entity.memberEntity,
        account: entity.accountEntity,
        projects: entity.projectList,
        projectIds: entity.projectIdList,
        partners: entity.partnerUserInfoList,
        vendorId: vendorId,
        token: entity.userToken,
        puser: puser,
      });
    }
  }

  yield next;
});

/*
 * ! 如果session过期
 * ! 根据cookie信息恢复
 */
app.use(interceptor.recover);

// 遍历目录加载路由模块
support.walk(__dirname).forEach(function (path) {
  if (/\broutes\.js$/.test([path])) {
    var router = require(path);
    app.use(router.routes());
    app.use(router.allowedMethods());
  }
});

app.listen(config.port, function () {
  logger.info('cluster worker %d started, pid is %d, listening on port: %d', cluster.worker.id, process.pid, config.port);
});